/*
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * ov4689 mipi sensor driver code for LomboTech
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 */

#include <linux/init.h>
#include <linux/module.h>

#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/viss_isp.h>

#include <media/lombo_viss.h>

#include <mach/debug.h>
#include "../ccc.h"

/* 0 35 RES_1920x1080_30fps_600Mbps */
/*100 99 1920 1080
 *102 3601 2ee0*/
static const cc_reg_t ov4689_1920x1080_30fps[] = {
	{ 0x0103, 0x01 },
	{ 0x3638, 0x00 },
	{ 0x0300, 0x00 },
	{ 0x0302, 0x19 },
	{ 0x0303, 0x00 },
	{ 0x0304, 0x03 },
	{ 0x030b, 0x00 },
	{ 0x030d, 0x1e },
	{ 0x030e, 0x04 },
	{ 0x030f, 0x01 },
	{ 0x0312, 0x01 },
	{ 0x031e, 0x00 },
	{ 0x3000, 0x20 },
	{ 0x3002, 0x00 },
	{ 0x3018, 0x32 },
	{ 0x3020, 0x93 },
	{ 0x3021, 0x03 },
	{ 0x3022, 0x01 },
	{ 0x3031, 0x0a },
	{ 0x303f, 0x0c },
	{ 0x3305, 0xf1 },
	{ 0x3307, 0x04 },
	{ 0x3309, 0x29 },
	{ 0x3500, 0x00 },
	{ 0x3501, 0x4c },
	{ 0x3502, 0x00 },
	{ 0x3503, 0x04 },
	{ 0x3504, 0x00 },
	{ 0x3505, 0x00 },
	{ 0x3506, 0x00 },
	{ 0x3507, 0x00 },
	{ 0x3508, 0x00 },
	{ 0x3509, 0x80 },
	{ 0x350a, 0x00 },
	{ 0x350b, 0x00 },
	{ 0x350c, 0x00 },
	{ 0x350d, 0x00 },
	{ 0x350e, 0x00 },
	{ 0x350f, 0x80 },
	{ 0x3510, 0x00 },
	{ 0x3511, 0x00 },
	{ 0x3512, 0x00 },
	{ 0x3513, 0x00 },
	{ 0x3514, 0x00 },
	{ 0x3515, 0x80 },
	{ 0x3516, 0x00 },
	{ 0x3517, 0x00 },
	{ 0x3518, 0x00 },
	{ 0x3519, 0x00 },
	{ 0x351a, 0x00 },
	{ 0x351b, 0x80 },
	{ 0x351c, 0x00 },
	{ 0x351d, 0x00 },
	{ 0x351e, 0x00 },
	{ 0x351f, 0x00 },
	{ 0x3520, 0x00 },
	{ 0x3521, 0x80 },
	{ 0x3522, 0x08 },
	{ 0x3524, 0x08 },
	{ 0x3526, 0x08 },
	{ 0x3528, 0x08 },
	{ 0x352a, 0x08 },
	{ 0x3602, 0x00 },
	{ 0x3603, 0x40 },
	{ 0x3604, 0x02 },
	{ 0x3605, 0x00 },
	{ 0x3606, 0x00 },
	{ 0x3607, 0x00 },
	{ 0x3609, 0x12 },
	{ 0x360a, 0x40 },
	{ 0x360c, 0x08 },
	{ 0x360f, 0xe5 },
	{ 0x3608, 0x8f },
	{ 0x3611, 0x00 },
	{ 0x3613, 0xf7 },
	{ 0x3616, 0x58 },
	{ 0x3619, 0x99 },
	{ 0x361b, 0x60 },
	{ 0x361c, 0x7a },
	{ 0x361e, 0x79 },
	{ 0x361f, 0x02 },
	{ 0x3632, 0x00 },
	{ 0x3633, 0x10 },
	{ 0x3634, 0x10 },
	{ 0x3635, 0x10 },
	{ 0x3636, 0x15 },
	{ 0x3646, 0x86 },
	{ 0x364a, 0x0b },
	{ 0x3700, 0x17 },
	{ 0x3701, 0x22 },
	{ 0x3703, 0x10 },
	{ 0x370a, 0x37 },
	{ 0x3705, 0x00 },
	{ 0x3706, 0x63 },
	{ 0x3709, 0x3c },
	{ 0x370b, 0x01 },
	{ 0x370c, 0x30 },
	{ 0x3710, 0x24 },
	{ 0x3711, 0x0c },
	{ 0x3716, 0x00 },
	{ 0x3720, 0x28 },
	{ 0x3729, 0x7b },
	{ 0x372a, 0x84 },
	{ 0x372b, 0xbd },
	{ 0x372c, 0xbc },
	{ 0x372e, 0x52 },
	{ 0x373c, 0x0e },
	{ 0x373e, 0x33 },
	{ 0x3743, 0x10 },
	{ 0x3744, 0x88 },
	{ 0x3745, 0xc0 },
	{ 0x374a, 0x43 },
	{ 0x374c, 0x00 },
	{ 0x374e, 0x23 },
	{ 0x3751, 0x7b },
	{ 0x3752, 0x84 },
	{ 0x3753, 0xbd },
	{ 0x3754, 0xbc },
	{ 0x3756, 0x52 },
	{ 0x375c, 0x00 },
	{ 0x3760, 0x00 },
	{ 0x3761, 0x00 },
	{ 0x3762, 0x00 },
	{ 0x3763, 0x00 },
	{ 0x3764, 0x00 },
	{ 0x3767, 0x04 },
	{ 0x3768, 0x04 },
	{ 0x3769, 0x08 },
	{ 0x376a, 0x08 },
	{ 0x376b, 0x20 },
	{ 0x376c, 0x00 },
	{ 0x376d, 0x00 },
	{ 0x376e, 0x00 },
	{ 0x3773, 0x00 },
	{ 0x3774, 0x51 },
	{ 0x3776, 0xbd },
	{ 0x3777, 0xbd },
	{ 0x3781, 0x18 },
	{ 0x3783, 0x25 },
	{ 0x3798, 0x1b },
	{ 0x3800, 0x01 },
	{ 0x3801, 0x88 },
	{ 0x3802, 0x00 },
	{ 0x3803, 0xe0 },
	{ 0x3804, 0x09 },
	{ 0x3805, 0x17 },
	{ 0x3806, 0x05 },
	{ 0x3807, 0x1f },
	{ 0x3808, 0x07 },
	{ 0x3809, 0x80 },
	{ 0x380a, 0x04 },
	{ 0x380b, 0x38 },
	{ 0x380c, 0x0c },
	{ 0x380d, 0xc4 },
	{ 0x380e, 0x04 },
	{ 0x380f, 0x8A },
	{ 0x3810, 0x00 },
	{ 0x3811, 0x08 },
	{ 0x3812, 0x00 },
	{ 0x3813, 0x04 },
	{ 0x3814, 0x01 },
	{ 0x3815, 0x01 },
	{ 0x3819, 0x01 },
	{ 0x3820, 0x00 },
	{ 0x3821, 0x06 },
	{ 0x3829, 0x00 },
	{ 0x382a, 0x01 },
	{ 0x382b, 0x01 },
	{ 0x382d, 0x7f },
	{ 0x3830, 0x04 },
	{ 0x3836, 0x01 },
	{ 0x3837, 0x00 },
	{ 0x3841, 0x02 },
	{ 0x3846, 0x08 },
	{ 0x3847, 0x07 },
	{ 0x3d85, 0x36 },
	{ 0x3d8c, 0x71 },
	{ 0x3d8d, 0xcb },
	{ 0x3f0a, 0x00 },
	{ 0x4000, 0xf1 },
	{ 0x4001, 0x40 },
	{ 0x4002, 0x04 },
	{ 0x4003, 0x14 },
	{ 0x400e, 0x00 },
	{ 0x4011, 0x00 },
	{ 0x401a, 0x00 },
	{ 0x401b, 0x00 },
	{ 0x401c, 0x00 },
	{ 0x401d, 0x00 },
	{ 0x401f, 0x00 },
	{ 0x4020, 0x00 },
	{ 0x4021, 0x10 },
	{ 0x4022, 0x06 },
	{ 0x4023, 0x13 },
	{ 0x4024, 0x07 },
	{ 0x4025, 0x40 },
	{ 0x4026, 0x07 },
	{ 0x4027, 0x50 },
	{ 0x4028, 0x00 },
	{ 0x4029, 0x02 },
	{ 0x402a, 0x06 },
	{ 0x402b, 0x04 },
	{ 0x402c, 0x02 },
	{ 0x402d, 0x02 },
	{ 0x402e, 0x0e },
	{ 0x402f, 0x04 },
	{ 0x4302, 0xff },
	{ 0x4303, 0xff },
	{ 0x4304, 0x00 },
	{ 0x4305, 0x00 },
	{ 0x4306, 0x00 },
	{ 0x4308, 0x02 },
	{ 0x4500, 0x6c },
	{ 0x4501, 0xc4 },
	{ 0x4502, 0x40 },
	{ 0x4503, 0x01 },
	{ 0x4601, 0x77 },
	{ 0x4800, 0x04 },
	{ 0x4813, 0x08 },
	{ 0x481f, 0x40 },
	{ 0x4829, 0x78 },
	{ 0x4837, 0x10 },
	{ 0x4b00, 0x2a },
	{ 0x4b0d, 0x00 },
	{ 0x4d00, 0x04 },
	{ 0x4d01, 0x42 },
	{ 0x4d02, 0xd1 },
	{ 0x4d03, 0x93 },
	{ 0x4d04, 0xf5 },
	{ 0x4d05, 0xc1 },
	{ 0x5000, 0xf3 },
	{ 0x5001, 0x11 },
	{ 0x5004, 0x00 },
	{ 0x500a, 0x00 },
	{ 0x500b, 0x00 },
	{ 0x5032, 0x00 },
	{ 0x5040, 0x00 },
	{ 0x5050, 0x0c },
	{ 0x5500, 0x00 },
	{ 0x5501, 0x10 },
	{ 0x5502, 0x01 },
	{ 0x5503, 0x0f },
	{ 0x8000, 0x00 },
	{ 0x8001, 0x00 },
	{ 0x8002, 0x00 },
	{ 0x8003, 0x00 },
	{ 0x8004, 0x00 },
	{ 0x8005, 0x00 },
	{ 0x8006, 0x00 },
	{ 0x8007, 0x00 },
	{ 0x8008, 0x00 },
	{ 0x3638, 0x00 },
	/* { 0x0100, 0x01 }, */
	/* c8 { 0x0300, 0x60 }, */
};

/* ov4689_mipi_2688x1520_30fps_4m_2lane_768mbps */
static const cc_reg_t ov4689_2688x1520_30fps[] = {
	{ 0x0103, 0x01 },
	{ 0x3638, 0x00 },
	{ 0x0300, 0x00 },
	{ 0x0302, 0x20 },
	{ 0x0303, 0x00 },
	{ 0x0304, 0x03 },
	{ 0x030b, 0x00 },
	{ 0x030d, 0x1e },
	{ 0x030e, 0x04 },
	{ 0x030f, 0x01 },
	{ 0x0312, 0x01 },
	{ 0x031e, 0x00 },
	{ 0x3000, 0x20 },
	{ 0x3002, 0x00 },
	{ 0x3018, 0x32 },
	{ 0x3020, 0x93 },
	{ 0x3021, 0x03 },
	{ 0x3022, 0x01 },
	{ 0x3031, 0x0a },
	{ 0x303f, 0x0c },
	{ 0x3305, 0xf1 },
	{ 0x3307, 0x04 },
	{ 0x3309, 0x29 },
	{ 0x3500, 0x00 },
	{ 0x3501, 0x60 },
	{ 0x3502, 0x00 },
	{ 0x3503, 0x04 },
	{ 0x3504, 0x00 },
	{ 0x3505, 0x00 },
	{ 0x3506, 0x00 },
	{ 0x3507, 0x00 },
	{ 0x3508, 0x00 },
	{ 0x3509, 0x80 },
	{ 0x350a, 0x00 },
	{ 0x350b, 0x00 },
	{ 0x350c, 0x00 },
	{ 0x350d, 0x00 },
	{ 0x350e, 0x00 },
	{ 0x350f, 0x80 },
	{ 0x3510, 0x00 },
	{ 0x3511, 0x00 },
	{ 0x3512, 0x00 },
	{ 0x3513, 0x00 },
	{ 0x3514, 0x00 },
	{ 0x3515, 0x80 },
	{ 0x3516, 0x00 },
	{ 0x3517, 0x00 },
	{ 0x3518, 0x00 },
	{ 0x3519, 0x00 },
	{ 0x351a, 0x00 },
	{ 0x351b, 0x80 },
	{ 0x351c, 0x00 },
	{ 0x351d, 0x00 },
	{ 0x351e, 0x00 },
	{ 0x351f, 0x00 },
	{ 0x3520, 0x00 },
	{ 0x3521, 0x80 },
	{ 0x3522, 0x08 },
	{ 0x3524, 0x08 },
	{ 0x3526, 0x08 },
	{ 0x3528, 0x08 },
	{ 0x352a, 0x08 },
	{ 0x3602, 0x00 },
	{ 0x3603, 0x40 },
	{ 0x3604, 0x02 },
	{ 0x3605, 0x00 },
	{ 0x3606, 0x00 },
	{ 0x3607, 0x00 },
	{ 0x3609, 0x12 },
	{ 0x360a, 0x40 },
	{ 0x360c, 0x08 },
	{ 0x360f, 0xe0 },
	{ 0x3608, 0x8f },
	{ 0x3611, 0x00 },
	{ 0x3613, 0xf7 },
	{ 0x3616, 0x58 },
	{ 0x3619, 0x99 },
	{ 0x361b, 0x60 },
	{ 0x361c, 0x7a },
	{ 0x361e, 0x79 },
	{ 0x361f, 0x02 },
	{ 0x3632, 0x00 },
	{ 0x3633, 0x10 },
	{ 0x3634, 0x10 },
	{ 0x3635, 0x10 },
	{ 0x3636, 0x10 },
	{ 0x3646, 0x86 },
	{ 0x364a, 0x0b },
	{ 0x3700, 0x17 },
	{ 0x3701, 0x22 },
	{ 0x3703, 0x10 },
	{ 0x370a, 0x37 },
	{ 0x3705, 0x00 },
	{ 0x3706, 0x63 },
	{ 0x3709, 0x3c },
	{ 0x370b, 0x01 },
	{ 0x370c, 0x30 },
	{ 0x3710, 0x24 },
	{ 0x3711, 0x0c },
	{ 0x3716, 0x00 },
	{ 0x3720, 0x28 },
	{ 0x3729, 0x7b },
	{ 0x372a, 0x84 },
	{ 0x372b, 0xbd },
	{ 0x372c, 0xbc },
	{ 0x372e, 0x52 },
	{ 0x373c, 0x0e },
	{ 0x373e, 0x33 },
	{ 0x3743, 0x10 },
	{ 0x3744, 0x88 },
	{ 0x3745, 0xc0 },
	{ 0x374a, 0x43 },
	{ 0x374c, 0x00 },
	{ 0x374e, 0x23 },
	{ 0x3751, 0x7b },
	{ 0x3752, 0x84 },
	{ 0x3753, 0xbd },
	{ 0x3754, 0xbc },
	{ 0x3756, 0x52 },
	{ 0x375c, 0x00 },
	{ 0x3760, 0x00 },
	{ 0x3761, 0x00 },
	{ 0x3762, 0x00 },
	{ 0x3763, 0x00 },
	{ 0x3764, 0x00 },
	{ 0x3767, 0x04 },
	{ 0x3768, 0x04 },
	{ 0x3769, 0x08 },
	{ 0x376a, 0x08 },
	{ 0x376b, 0x20 },
	{ 0x376c, 0x00 },
	{ 0x376d, 0x00 },
	{ 0x376e, 0x00 },
	{ 0x3773, 0x00 },
	{ 0x3774, 0x51 },
	{ 0x3776, 0xbd },
	{ 0x3777, 0xbd },
	{ 0x3781, 0x18 },
	{ 0x3783, 0x25 },
	{ 0x3798, 0x1b },
	{ 0x3800, 0x00 },
	{ 0x3801, 0x08 },
	{ 0x3802, 0x00 },
	{ 0x3803, 0x04 },
	{ 0x3804, 0x0a },
	{ 0x3805, 0x97 },
	{ 0x3806, 0x05 },
	{ 0x3807, 0xfb },
	{ 0x3808, 0x0a },
	{ 0x3809, 0x80 },
	{ 0x380a, 0x05 },
	{ 0x380b, 0xf0 },
	{ 0x380c, 0x0A },
	{ 0x380d, 0x14 },
	{ 0x380e, 0x06 },
	{ 0x380f, 0x12 },
	{ 0x3810, 0x00 },
	{ 0x3811, 0x08 },
	{ 0x3812, 0x00 },
	{ 0x3813, 0x04 },
	{ 0x3814, 0x01 },
	{ 0x3815, 0x01 },
	{ 0x3819, 0x01 },
	{ 0x3820, 0x00 },
	{ 0x3821, 0x06 },
	{ 0x3829, 0x00 },
	{ 0x382a, 0x01 },
	{ 0x382b, 0x01 },
	{ 0x382d, 0x7f },
	{ 0x3830, 0x04 },
	{ 0x3836, 0x01 },
	{ 0x3837, 0x00 },
	{ 0x3841, 0x02 },
	{ 0x3846, 0x08 },
	{ 0x3847, 0x07 },
	{ 0x3d85, 0x36 },
	{ 0x3d8c, 0x71 },
	{ 0x3d8d, 0xcb },
	{ 0x3f0a, 0x00 },
	{ 0x4000, 0xf1 },
	{ 0x4001, 0x40 },
	{ 0x4002, 0x04 },
	{ 0x4003, 0x14 },
	{ 0x400e, 0x00 },
	{ 0x4011, 0x00 },
	{ 0x401a, 0x00 },
	{ 0x401b, 0x00 },
	{ 0x401c, 0x00 },
	{ 0x401d, 0x00 },
	{ 0x401f, 0x00 },
	{ 0x4020, 0x00 },
	{ 0x4021, 0x10 },
	{ 0x4022, 0x07 },
	{ 0x4023, 0xcf },
	{ 0x4024, 0x09 },
	{ 0x4025, 0x60 },
	{ 0x4026, 0x09 },
	{ 0x4027, 0x6f },
	{ 0x4028, 0x00 },
	{ 0x4029, 0x02 },
	{ 0x402a, 0x06 },
	{ 0x402b, 0x04 },
	{ 0x402c, 0x02 },
	{ 0x402d, 0x02 },
	{ 0x402e, 0x0e },
	{ 0x402f, 0x04 },
	{ 0x4302, 0xff },
	{ 0x4303, 0xff },
	{ 0x4304, 0x00 },
	{ 0x4305, 0x00 },
	{ 0x4306, 0x00 },
	{ 0x4308, 0x02 },
	{ 0x4500, 0x6c },
	{ 0x4501, 0xc4 },
	{ 0x4502, 0x40 },
	{ 0x4503, 0x01 },
	{ 0x4601, 0xA7 },
	{ 0x4800, 0x04 },
	{ 0x4813, 0x08 },
	{ 0x481f, 0x40 },
	{ 0x4829, 0x78 },
	{ 0x4837, 0x14 },
	{ 0x4b00, 0x2a },
	{ 0x4b0d, 0x00 },
	{ 0x4d00, 0x04 },
	{ 0x4d01, 0x42 },
	{ 0x4d02, 0xd1 },
	{ 0x4d03, 0x93 },
	{ 0x4d04, 0xf5 },
	{ 0x4d05, 0xc1 },
	{ 0x5000, 0xf3 },
	{ 0x5001, 0x11 },
	{ 0x5004, 0x00 },
	{ 0x500a, 0x00 },
	{ 0x500b, 0x00 },
	{ 0x5032, 0x00 },
	{ 0x5040, 0x00 },
	{ 0x5050, 0x0c },
	{ 0x5500, 0x00 },
	{ 0x5501, 0x10 },
	{ 0x5502, 0x01 },
	{ 0x5503, 0x0f },
	{ 0x8000, 0x00 },
	{ 0x8001, 0x00 },
	{ 0x8002, 0x00 },
	{ 0x8003, 0x00 },
	{ 0x8004, 0x00 },
	{ 0x8005, 0x00 },
	{ 0x8006, 0x00 },
	{ 0x8007, 0x00 },
	{ 0x8008, 0x00 },
	{ 0x3638, 0x00 },
	/* { 0x0100, 0x01 }, */
};

static struct cc_power_act power_up_act[] = {
	{CC_RESET,  CC_DOWN},
	{CC_DELAY, 10},
	{CC_RESET,  CC_UP},
	{CC_DELAY, 10},
	{CC_PWDN, CC_UP},
	{CC_DELAY, 5},
};

static struct cc_power_act power_down_act[] = {
	{CC_PWDN, CC_DOWN},
	{CC_DELAY, 5},
	{CC_RESET,  CC_DOWN},
};

#define OV4689_1080P_WIDTH		1920
#define OV4689_1080P_HEIGHT		1080

#define OV4689_1520P_WIDTH		2688
#define OV4689_1520P_HEIGHT		1520

static const struct cc_frame_cfg frame_cfgs[] = {
	{
		.width = OV4689_1080P_WIDTH,
		.height = OV4689_1080P_HEIGHT,
		.framerate = { 1, 30 },	/* 30fps */
		.mfreq = 600000000,	/* mipi-csi clock */
		.vts = 1162,
		.hts = 3268,
		.pclk = 114 * 1000 * 1000,
		.dlanes = 2,
		.reg_cfgs[0].regs = ov4689_1920x1080_30fps,
		.reg_cfgs[0].regs_cnt = ARRAY_SIZE(ov4689_1920x1080_30fps),
		.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},
	{
		.width = OV4689_1520P_WIDTH,
		.height = OV4689_1520P_HEIGHT,
		.framerate = { 1, 30 },	/* 30fps */
		.mfreq = 768000000,	/* mipi-csi clock */
		.vts = 1554,
		.hts = 2580,
		.pclk = 120 * 1000 * 1000,
		.dlanes = 2,
		.reg_cfgs[0].regs = ov4689_2688x1520_30fps,
		.reg_cfgs[0].regs_cnt = ARRAY_SIZE(ov4689_2688x1520_30fps),
		.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},
};

static int sensor_detect(struct cc_sensor_info *cc_si)
{
	u8 tmp[2];
	int ret = 0;
	u16 id;

	ret = cc_si->read_reg(cc_si, 0x300a, &tmp[0]);
	if (ret != 0) {
		PRT_ERR("tmp[0]=%x\n", tmp[0]);
		return -ENODEV;
	}
	ret = cc_si->read_reg(cc_si, 0x300b, &tmp[1]);
	if (ret != 0) {
		PRT_ERR("tmp[0]=%x,tmp[1]=%x\n", tmp[0], tmp[1]);
		return -ENODEV;
	}
	id = (tmp[0] << 8) | tmp[1];
	PRT_INFO("id: 0x%04x.\n", id);

	if (id != 0x4688) {
		PRT_ERR("ID wrong! (0x%04x != 0x4688)\n", id);
		return -ENODEV;
	}

	return 0;
}

static int sensor_init(struct cc_sensor_info *cc_si)
{
	struct timespec ts_s;
	struct timespec ts_f;
	int ret = 0;

	PRT_INFO("\n");

	ret = sensor_detect(cc_si);
	if (ret != 0)
		return ret;

	ktime_get_ts(&ts_s);
#if 0
	ret = cc_si->write_reg_list(cc_si, ov4689_1920x1080_30fps,
					ARRAY_SIZE(ov4689_1920x1080_30fps));
	if (ret != 0) {
		PRT_ERR("write %s init_reg_list failed!\n", cc_si->name);
		return -ENODEV;
	}
#endif
	ktime_get_ts(&ts_f);

	PRT_INFO("write %s init_reg_list finish.\n", cc_si->name);
	PRT_INFO("timeused %d %d\n", (int)(ts_f.tv_sec - ts_s.tv_sec),
		(int)(ts_f.tv_nsec - ts_s.tv_nsec));

#if 0
	{
	u8 value = 0;
	int i;

	for (i = 0; i < ARRAY_SIZE(init_reg_list); i++) {
		if (init_reg_list[i].reg == CC_REG_DELAY)
			continue;

		ret = cc_si->read_reg(cc_si, init_reg_list[i].reg, &value);
		if (ret) {
			PRT_ERR("%s read i %d reg 0x%02x error\n",
				cc_si->name, i, init_reg_list[i].reg);
			return ret;
		}

		if (init_reg_list[i].val != value) {
			PRT_ERR("%s %d 0x%02x (0x%02x) != (0x%02x)\n",
				cc_si->name, i, init_reg_list[i].reg,
				value, init_reg_list[i].val);
		}
	}
	}
#endif

	return 0;
}

static int sensor_set_stream(struct cc_sensor_info *cc_si, int on)
{
	int ret = 0;

	if (on) {
		ret = cc_si->write_reg(cc_si, 0x0100, 0x01);
		if (ret)
			PRT_ERR("%d\n", on);
	} else {
		ret = cc_si->write_reg(cc_si, 0x0100, 0x00);
		if (ret)
			PRT_ERR("%d\n", on);
	}

	return ret;
}


static int get_reg(struct cc_sensor_info *cc_si,
			struct viss_isp_reg_data *reg_data)
{
	u8 val = 0;
	int ret = 0;

	/* read */
	ret = cc_si->read_reg(cc_si, reg_data->reg_add, &val);
	reg_data->reg_value = val;
#if 0
	PRT_INFO("addr: 0x%x, val: 0x%02x.\n",
		reg_data->reg_add, reg_data->reg_value);
#endif
	return ret;
}

static int set_reg(struct cc_sensor_info *cc_si,
			struct viss_isp_reg_data *reg_data)
{
	u8 val = reg_data->reg_value;
	u8 read_val;
	int ret;

	/* write */
	ret = cc_si->write_reg(cc_si, reg_data->reg_add, val);
	PRT_DBG("addr=0x%x,val=0x%02x.\n",
		reg_data->reg_add, reg_data->reg_value);

	ret = cc_si->read_reg(cc_si, reg_data->reg_add, &read_val);
	if (read_val != val)
		PRT_DBG("ret=%d,reg_add=0x%x,val=0x%02x,read_val=0x%02x\n",
			ret, reg_data->reg_add, val, read_val);

	return ret;
}

static int viss_get_sensor_fmt_info(struct cc_sensor_info *cc_si,
		struct viss_isp_sensor_fmt_info *sensor_fmt_info)
{
	int ret = 0;

	sensor_fmt_info->vts = cc_si->cur_frmcfg->vts;
	sensor_fmt_info->hts = cc_si->cur_frmcfg->hts;
	sensor_fmt_info->pclk = cc_si->cur_frmcfg->pclk;
	sensor_fmt_info->hoffset = cc_si->cur_frmcfg->left;
	sensor_fmt_info->voffset = cc_si->cur_frmcfg->top;
	sensor_fmt_info->width = cc_si->cur_frmcfg->width;
	sensor_fmt_info->height = cc_si->cur_frmcfg->height;
	sensor_fmt_info->bayer_mode = ISP_PATTERN_BGGR;
	sensor_fmt_info->buswidth = ISP_INPUT_10BITS;

	return ret;
}

static int viss_isp_set_shutter(struct cc_sensor_info *cc_si,
				__u32 shutter_time)
{
	int ret = 0;
	u16 vmax = cc_si->cur_frmcfg->vts;

	if (shutter_time / 16 > vmax - 4)
		vmax = shutter_time / 16 + 4;

	ret = cc_si->write_reg(cc_si, 0x380f, (vmax & 0xff));
	ret = cc_si->write_reg(cc_si, 0x380e, (vmax >> 8));

	ret = cc_si->write_reg(cc_si, 0x3500, ((shutter_time >> 16) & 0xf));
	ret = cc_si->write_reg(cc_si, 0x3501, ((shutter_time >> 8) & 0xff));
	ret = cc_si->write_reg(cc_si, 0x3502, ((shutter_time >> 0) & 0xff));

	return ret;
}

static int viss_isp_set_gain(struct cc_sensor_info *cc_si, __u32 gain)
{
	int ret = 0;
	u32 again = gain / 2;
	u8 gainhigh, gainlow;

	if (again > 16 * 128 - 1)
		again = 16 * 128 - 1;

	if (again < 2 * 128) {
		gainhigh = 0;
		gainlow = again;
	} else if (again < 4 * 128) {
		gainhigh = 1;
		gainlow = again / 2 - 8;
	} else if (again < 8 * 128) {
		gainhigh = 3;
		gainlow = again / 4 - 12;
	} else {
		gainhigh = 7;
		gainlow = again / 8 - 8;
	}

	ret = cc_si->write_reg(cc_si, 0x3508, gainhigh);
	ret = cc_si->write_reg(cc_si, 0x3509, gainlow);

	return ret;
}

static int viss_isp_exp_ctrl(struct cc_sensor_info *cc_si,
				struct viss_isp_exp_gain *exp_gain)
{
	int ret = 0;

	ret = viss_isp_set_shutter(cc_si, exp_gain->exp_time);
	if (ret < 0) {
		PRT_ERR("viss_isp_set_shutter failed %d.\n", ret);
		PRT_ERR("viss_isp_exp_ctrl %u, %u.\n",
			exp_gain->exp_time, exp_gain->exp_gain);
		return -ENODEV;
	}

	ret = viss_isp_set_gain(cc_si, exp_gain->exp_gain);
	if (ret < 0) {
		PRT_ERR("viss_isp_set_gain failed %d.\n", ret);
		PRT_ERR("viss_isp_exp_ctrl %u, %u.\n",
			exp_gain->exp_time, exp_gain->exp_gain);
		return -ENODEV;
	}

	return ret;
}

static long sensor_ioctl(struct cc_sensor_info *cc_si, unsigned int cmd,
				void *arg)
{
	int ret = 0;

	switch (cmd) {
	case VIDIOC_VISS_GET_SENSOR:
		ret = get_reg(cc_si, (struct viss_isp_reg_data *)arg);
		break;
	case VIDIOC_VISS_SET_SENSOR:
		ret = set_reg(cc_si, (struct viss_isp_reg_data *)arg);
		break;
	case VIDIOC_VISS_ISP_EXP_CTRL:
		ret = viss_isp_exp_ctrl(cc_si,
			(struct viss_isp_exp_gain *)arg);
		break;
	case VIDIOC_VISS_ISP_SENSOR_FMT_INFO:
		ret = viss_get_sensor_fmt_info(cc_si,
			(struct viss_isp_sensor_fmt_info *)arg);
		break;
	default:
		return -EINVAL;
	}
	return ret;
}


/*
 * ctrl for sensor finish
 */

#define DRIVER_NAME "ov4689_mipi"

static int sensor_probe(struct platform_device *pdev)
{
	struct cc_sensor_info *cc_si = NULL;
	int ret = 0;

	PRT_INFO("*********probe begin\n");

	cc_si = devm_kzalloc(&pdev->dev, sizeof(*cc_si), GFP_KERNEL);
	if (cc_si == NULL) {
		PRT_ERR("Can't alloc memory!\n");
		return -ENOMEM;
	}

	cc_si->reg_bits = 16;
	cc_si->val_bits = 8;

	cc_si->name = DRIVER_NAME;

	cc_si->power_up_act = power_up_act;
	cc_si->power_down_act = power_down_act;
	cc_si->npwr_up_acts = ARRAY_SIZE(power_up_act);
	cc_si->npwr_dn_acts = ARRAY_SIZE(power_down_act);

	cc_si->framecfgs = frame_cfgs;
	cc_si->nfrmcfgs = ARRAY_SIZE(frame_cfgs);

	ret = cc_sensor_probe(pdev, cc_si);
	if (ret < 0)
		return ret;

	cc_si->detect = sensor_detect;
	cc_si->sensor_init = sensor_init;
	cc_si->set_stream = sensor_set_stream;
	cc_si->sensor_ioctl = sensor_ioctl;

	PRT_INFO("probe end\n");
	return 0;
}

static int sensor_remove(struct platform_device *pdev)
{
	struct cc_sensor_info *cc_si = platform_get_drvdata(pdev);

	cc_sensor_remove(pdev);

	devm_kfree(&pdev->dev, cc_si);

	return 0;
}

static const struct of_device_id ov4689_mipi_of_match[] = {
	{
		.compatible = DRIVER_NAME,
	},
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, ov4689_mipi_of_match);

static struct platform_driver ov4689_mipi_driver = {
	.driver = {
		.of_match_table	= ov4689_mipi_of_match,
		.name		= DRIVER_NAME,
		.owner		= THIS_MODULE,
	},
	.probe = sensor_probe,
	.remove = sensor_remove,
};

module_platform_driver(ov4689_mipi_driver);

MODULE_DESCRIPTION("OV4689-MIPI sensor driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);

